#! /usr/bin/python

#* ========================================================================= *#
#* ------------------------------------------------------------------------- *#
##
##  \file        doctool.py
##  \date        November 2011
##  \author      TNick
##
##  \brief        Script for dealing with literate programming
##
##
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
## Please read README and COPYING in root folder @n
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @n
##
#* ------------------------------------------------------------------------- *#
#* ========================================================================= *#
##
##
##
##
#*  INCLUDES    ------------------------------------------------------------ *#

import    subprocess
import    fileinput, string, sys, time
import    os
import    platform
from      time import gmtime, strftime

#*  INCLUDES    ============================================================ *#
##
##
##
##
#*  DATA    ---------------------------------------------------------------- *#


g_dst_dbf = {}
g_inc_mark = "\\include{"
g_tbl_mark = "\\begin{tabular}{"
g_tbl_end_mark = "\\end{tabular}"
g_tbl_line_end = "\\tabularnewline\n"
g_dest_file = None
g_dest_latex = None
g_dest_key = None
g_src_dir = ""
g_dst_dir = ""
g_build_dir = ""

g_file_header = \
'''/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \\file         ^^^FILE^^^
  \\date         Mar 2012
  \\author       TNick

  \\brief        Generated by automated process - literate programming


*//*


 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read COPYING and README files in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
'''


g_file_footer = \
'''/* ------------------------------------------------------------------------- */
/* ========================================================================= */
'''


g_guard_top = \
'''#ifndef __^^^FILE^^^_INC__
#define __^^^FILE^^^_INC__

'''

g_guard_btm = '''
#endif /* __^^^FILE^^^_INC__ */
'''

#*  DATA    ================================================================ *#
##
##
##
##
#*  FUNCTIONS    ----------------------------------------------------------- *#

#* ------------------------------------------------------------------------- *#
def     getDestFile(f_key):
    global g_dest_file
    global g_dest_key
    global g_dst_dbf
    global g_inc_mark
    global g_src_dir
    global g_dest_latex
    global g_dst_dir
    global g_build_dir
    
    if ( len( f_key ) == 0 ):
		return

    try:
        g_dest_file=g_dst_dbf[f_key]
    except KeyError:
        [file_path,file_name]=os.path.split( f_key )
        file_path = os.path.join( g_dst_dir, file_path )
        if ( len( file_path ) > 0 ):
            if ( os.path.exists( file_path ) == False ):
                os.makedirs( file_path )
        g_dest_file = open( os.path.join( g_dst_dir, f_key ), 'w' )
        g_dst_dbf[f_key] = g_dest_file
        if ( f_key.endswith( ".h" ) \
          or f_key.endswith( ".c" )  \
          or f_key.endswith( ".cc" )  ):
            g_dst_dbf[f_key].write( \
              g_file_header.replace( "^^^FILE^^^", f_key ) )
            if ( f_key.endswith( ".h" ) ):
                g_dst_dbf[f_key].write( \
                  g_guard_top.replace( \
                    "^^^FILE^^^", 
                    file_name[:-2].upper() ) )
            else:
                g_dst_dbf[f_key].write( "#include <libuconn/uconn.h>\n\n" )

    g_dest_key = f_key
    
#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def     closeAllDest():
    global g_dest_file
    global g_dest_key
    global g_dst_dbf
    global g_inc_mark
    global g_src_dir
    global g_dest_latex
    global g_dst_dir
    global g_build_dir


    for f_key in g_dst_dbf:
        [file_path,file_name]=os.path.split( f_key )
        if ( f_key.endswith( ".h" ) ):
            g_dst_dbf[f_key].write( g_guard_btm.replace( \
              "^^^FILE^^^", file_name[:-2].upper() ) )
        if ( f_key.endswith( ".h" ) \
          or f_key.endswith( ".c" )  \
          or f_key.endswith( ".cc" )  ):
            g_dst_dbf[f_key].write( g_file_footer )
        g_dst_dbf[f_key].close()
        g_dst_dbf[f_key] = None
    
#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def scanTableLine ( line ):
    """ divides the line into component cells """
    ret_list = []
    b_code = False
    crt_str = ""
    for letter in line:
        if letter == "\\":
            b_code = True
        elif b_code:
            if letter == "&":
                pass
            elif letter == "\\":
                pass
            elif letter == "n":
                letter = "\n"
            elif letter == "t":
                letter = "\t"
            elif letter == "r":
                letter = "\r"
            elif letter == "_":
                letter = "_"
            else:
                print "unknown escape sequence in table string: \\" + letter
                raise ValueError
            crt_str = crt_str + letter
            b_code = False
        elif letter == "&":
            ret_list.append( crt_str.strip() )
            crt_str = ""
        else:
            crt_str = crt_str + letter
    if ( len( crt_str ) > 0 ):
        ret_list.append( crt_str.strip() )
    
    return ret_list
    
#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def     scan_tex_file(src_latex_file):
    global g_dest_file
    global g_dest_key
    global g_dst_dbf
    global g_inc_mark
    global g_src_dir
    global g_dest_latex
    global g_dst_dir
    global g_build_dir
    
    orig_src = os.path.join( g_src_dir, src_latex_file )
    moved_src = os.path.join( g_dest_latex, src_latex_file )
    print "  - processing", src_latex_file
    
    if ( os.path.exists( moved_src ) ):
        os.remove( moved_src )
    os.rename( orig_src, moved_src )
    
    # read input latex file
    file = open( moved_src, 'r' )
    b_in = False
    tbl_buff = []
    for line in file:
        if ( line.startswith( "\\begin{lstlisting}" ) ):
            b_in = True
        elif ( line.startswith( "\\end{lstlisting}" ) ):
            b_in = False
        elif ( line.startswith( g_inc_mark ) ):
            # recursive scan of files
            line=line[len(g_inc_mark):line.index("}")] + ".tex"
            scan_tex_file( line )
            continue
        elif b_in:
            # file content
            if ( len( line ) > 4):
                if ( line.startswith( "##" ) and line.endswith( "##\n" ) ):
                    # we're changing the current output file
                    line = line[2:len(line)-4].strip()
                    getDestFile( line )
                    print line
                    continue
            g_dest_file.write( line )

            
    file.close()
    return 0

#* ========================================================================= *#

#* ------------------------------------------------------------------------- *#
def     main( src_lyx_file, dst_dir, build_dir ):
    """ Main function that is called when the module is loaded by itself
    """
    global g_dest_file
    global g_dest_key
    global g_dst_dbf
    global g_inc_mark
    global g_src_dir
    global g_dst_dir
    global g_dest_latex
    global g_build_dir

    # the source file to use
    [g_src_dir,src_lyx_file] = os.path.split( src_lyx_file )
    g_src_dir = os.path.abspath( g_src_dir )
    g_dst_dir = os.path.abspath( dst_dir )
    g_build_dir = os.path.abspath( build_dir )
    if ( os.path.exists( g_dst_dir ) == False ):
        os.makedirs( g_dst_dir )
    if ( os.path.exists( g_build_dir ) == False ):
        os.makedirs( g_build_dir )
    os.chdir( g_dst_dir )
    g_dest_latex = os.path.join( g_dst_dir, "doc/tex" )
    if ( os.path.exists( g_dest_latex ) == False ):
        os.makedirs( g_dest_latex )
    
    print "Source dir", g_src_dir
    print "Destination dir", g_dst_dir
    print "Build dir", g_build_dir
    print "Source file", src_lyx_file
    src_lyx_file_full = os.path.join( g_src_dir, src_lyx_file )


    if ( platform.uname()[0] == "Windows" ):
        try:
            LYX_Prog = os.environ['LYX']
        except KeyError:
            print( " Could not find LyX. Set LYX environment variable to point to it." )
            return -1
        print "Using LyX from ", LYX_Prog
        
        try:
            MINGW_Dir = os.environ['MINGW_DIR']
        except KeyError:
            print( " Could not find MinGW. Set MINGW_DIR environment variable to point to it." )
            return -1
        print "Using MinGW from ", MINGW_Dir
        os.environ['PATH'] = MINGW_Dir + "\\bin" + ";" + os.environ['PATH']
        print "PATH = ", os.environ['PATH']
        
        try:
            ASTYLE_Dir = os.environ['ASTYLE_PATH']
        except KeyError:
            print( " Could not find AStyle. Set ASTYLE_PATH environment variable to point to it." )
            return -1
        ASTYLE = ASTYLE_Dir + "\\bin\\astyle.exe \"" + g_dst_dir + "\" "
        print "Using AStyle from ", ASTYLE
        
        try:
            CMAKE_Dir = os.environ['CMAKE_PATH']
        except KeyError:
            print( " Could not find CMake. Set CMAKE_PATH environment variable to point to it." )
            return -1
        CMAKE = "\"" + CMAKE_Dir + "\\bin\\cmake.bat\""
        print "Using CMake from ", CMAKE
        
        # a cmake.bat placed in same directory as cmake.exe should take precedence
        #    @cmake.exe -G "MinGW Makefiles" %*
        # Otherwise the cmake for windows will assume nmake toolchain 
        # and will not work in current config.
        #
        # Nmake way is not used because it complans with 10.0 
        # about redistributable.
        #
        
        # MAKE_PROG="nmake"
        MAKE_PROG=MINGW_Dir + "\\bin\\mingw32-make.exe"
    else:
        LYX_Prog="lyx"
        ASTYLE="astyle"
        CMAKE="cmake"
        MAKE_PROG="make"
        
    # resulting latex file
    src_latex_file, fileExtension = os.path.splitext( src_lyx_file )
    src_latex_file = src_latex_file + ".tex"
    
    # resulting source file
    #dst_c_file, fileExtension = os.path.splitext( src_lyx_file )
    #dst_c_file = os.path.join( g_dst_dir, dst_c_file + ".c" )
    dst_c_file = ""
    
    # generate latex file
    print "\n\n\nRunning LyX over document..."
    return_code = subprocess.call( "\"" + LYX_Prog + "\" --export latex " + src_lyx_file_full, shell=True)
    print ( "LyX returns " + str( return_code ) )
    if ( return_code != 0 ):
        return return_code
    
    
    # read input latex file
    print "\n\n\nCreating source files..."
    getDestFile( dst_c_file )
    scan_tex_file( src_latex_file )
    closeAllDest()


    # beautify the code
    print "\n\n\nRunning AStyle..."
	#return_code = subprocess.call( ASTYLE + \
	#    " --recursive --style=linux -s4 --quiet " + \
	#    " --brackets=break --break-blocks --pad-oper " + \
	#    " --pad-paren-in --unpad-paren --unpad-paren " + \
	#    " --add-brackets --convert-tabs --align-pointer=middle " + \
	#    " --suffix=none  \"*.c\" \"*.h\"", shell=True)
        
        
    # attempt to configure
    print "\n\n\nConfiguring with CMake..."
    os.chdir( g_build_dir )
    return_code = subprocess.call( CMAKE + " " + g_dst_dir + " -DCMAKE_BUILD_TYPE=Debug", shell=True)
    #return_code = subprocess.call( CMAKE + " -DCMAKE_BUILD_TYPE=Release" + g_dst_dir, shell=True)
    print ( "cmake returns " + str( return_code ) )
    if ( return_code != 0 ):
        return return_code
    
    
    # attempt to build
    print "\n\n\nBuilding..."
    return_code = subprocess.call( MAKE_PROG + " VERBOSE=1", shell=True)
    print ( "make returns " + str( return_code ) )
    if ( return_code != 0 ):
        return return_code
        
    # test application behaviour
    # print "\n\n\nTesting..."
    #return_code = subprocess.call( MAKE_PROG + " test", shell=True)
    #print ( "test returns " + str( return_code ) )
    #if ( return_code != 0 ):
    #    return return_code       
    
    # finally exit
    return 0
#* ========================================================================= *#



if __name__ == "__main__":
    # assume
    #
    # +-- source
    # |    |
    # |    +-- doc
    # |         |
    # |         +-- blue-print-uconn.lyx
    # +-- tools
    # |    |
    # |    +-- scripts
    # |         |
    # |         +-- doctool.py  <- we start here by default
    # +-- build
    #
    #
    
    src_f = "../../source/doc/blue-print-uconn.lyx"
    dest_d = "../../source"
    build_d = "../../build"
    
    if len( sys.argv ) == 1:
        pass
    elif len( sys.argv ) == 2:
        src_f = sys.argv[1]
    elif len( sys.argv ) == 3:
        src_f = sys.argv[1]
        dest_d = sys.argv[2]
    elif len( sys.argv ) == 4:
        src_f = sys.argv[1]
        dest_d = sys.argv[2]
        build_d = sys.argv[3]
    else:
        print    '''The script only allows three arguments:
        - main document
        - destination directory (location of souce files)
        - build directory'''
        sys.exit( -1 )
        
    sys.exit( main( src_f, dest_d, build_d ) )

#*  FUNCTIONS    =========================================================== *#
##
##
##
##
#* ------------------------------------------------------------------------- *#
#* ========================================================================= *#
